package de.jabc.sib.bio.ebi;

import java.util.Map;
import java.util.HashMap;

import org.apache.log4j.Logger;

import de.metaframe.jabc.framework.sib.annotation.SIBClassLoader;
import de.metaframe.jabc.framework.util.SwingUtil;

/**
 * This class provides icons for the SIBs. It features caching of already loaded
 * icons (as required by the SVG extension of the jABC) and error logging.
 * <p>
 * SIB libraries should typically create an instance of this class once per
 * entire library. This might be accomplished by storing the icon cache instance
 * in a static field of some class (e.g. the superclass of all SIBs):
 * 
 * <pre>
 * protected static final IconCache icons = new IconCache(MySIB.class);
 * </pre>
 * 
 * @author Benjamin Bentmann
 * @version $Revision: 2576 $ $Date: 2008-01-13 12:01:43 +0100 (Sun, 13 Jan 2008) $
 */
@SIBClassLoader
public class IconCache {

	/**
	 * The logger for this class.
	 */
	private static Logger log = Logger.getLogger(IconCache.class);

	/**
	 * The class loader to use for looking up the icon resources.
	 */
	private ClassLoader loader;

	/**
	 * The qualified name of the base directory used to resolve icon names. If
	 * not empty, this field must end with a slash '/'.
	 */
	private String directory;

	/**
	 * The cache of already loaded icons, indexed by icon name.
	 */
	private Map<String, Object> icons = new HashMap<String, Object>();

	/**
	 * Creates a new icon cache for the specified caller. The class loader of
	 * the given class object will be used to load icons. The base directory
	 * used to resolve icon names will be set to the subdirectory "icons" of the
	 * package directory hosting the class object. For example, if the given
	 * class object is named "de.jabc.sib.MySIB" all icons to be loaded by this
	 * instance must reside inside the directory "de/jabc/sib/icons".
	 * 
	 * @param caller
	 *            The class object used to configure this instance, in most
	 *            cases the class of the caller.
	 * @throws NullPointerException
	 *             If the class argument is {@code null}.
	 */
	public IconCache(Class<?> caller) {
		this(caller.getClassLoader(), getIconDirectory(caller));
	}

	/**
	 * Gets the path to the icon directory for the specified class.
	 * 
	 * @param caller
	 *            The class object used to derive the path to the icon
	 *            directory.
	 * @return The icon directory.
	 * @throws NullPointerException
	 *             If the class argument is {@code null}.
	 */
	private static String getIconDirectory(Class<?> caller) {
		return caller.getPackage().getName().replace('.', '/') + "/icons/";
	}

	/**
	 * Creates a new icon cache with the specified configuration.
	 * 
	 * @param loader
	 *            The class loader to use for resource lookup.
	 * @param directory
	 *            The base directory used to resolve icon names.
	 */
	protected IconCache(ClassLoader loader, String directory) {
		this.loader = loader;
		this.directory = (directory != null) ? directory : "";
		if (this.directory.length() > 0 && !this.directory.endsWith("/")) {
			this.directory += '/';
		}
		if (log.isDebugEnabled()) {
			log.debug("Created new icon cache rooted at " + this.directory
					+ " (" + this.loader + ")");
		}
	}

	/**
	 * Gets the specified SIB icon. Each icon should be provided as a vector
	 * graphic (SVG) and a raster image (PNG). Therefore, the provided icon name
	 * only denotes the base name of the icon file. For example, the icon name
	 * "sib" resolves to the files "sib.png" and "sib.svg". Which of them gets
	 * actually loaded depends on the status of the SVG support.
	 * 
	 * @param name
	 *            The name of requested icon.
	 * @return The SIB icon.
	 */
	public Object get(String name) {
		Object icon = this.icons.get(name);
		if (icon == null) {
			String svg = this.directory + name + ".svg";
			String png = this.directory + name + ".png";
			try {
				icon = SwingUtil.loadIcon(svg, png, this.loader);
			} catch (Exception e) {
				log.warn("Failed to load icon \"" + name + "\" from "
						+ this.directory, e);
				icon = e;
			}
			this.icons.put(name, icon);
		}
		return icon;
	}

}
